package frsf.cidisi.exercise.laberinto.search;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

import frsf.cidisi.exercise.laberinto.search.actions.Avanzar;
import frsf.cidisi.exercise.laberinto.search.actions.GirarDerecha;
import frsf.cidisi.exercise.laberinto.search.actions.GirarIzquierda;
import frsf.cidisi.exercise.laberinto.search.util.Entorno;
import frsf.cidisi.exercise.laberinto.search.util.Par;
import frsf.cidisi.faia.agent.Action;
import frsf.cidisi.faia.agent.Perception;
import frsf.cidisi.faia.agent.search.Problem;
import frsf.cidisi.faia.agent.search.SearchAction;
import frsf.cidisi.faia.agent.search.SearchBasedAgent;
import frsf.cidisi.faia.solver.search.AStarSearch;
import frsf.cidisi.faia.solver.search.BreathFirstSearch;
import frsf.cidisi.faia.solver.search.IEstimatedCostFunction;
import frsf.cidisi.faia.solver.search.IStepCostFunction;
import frsf.cidisi.faia.solver.search.NTree;
import frsf.cidisi.faia.solver.search.Search;
import frsf.cidisi.faia.solver.search.Strategy;
import frsf.cidisi.faia.solver.search.UniformCostSearch;

public class Agente extends SearchBasedAgent {
	
	Vector<NTree> solucion;

    public Agente() {

    	solucion = null;
    	
        // The Agent Goal
        Salida agGoal = new Salida();

        // The Agent State
        EstadoAgente agState = new EstadoAgente();
        this.setAgentState(agState);

        // Create the operators
        Vector<SearchAction> operators = new Vector<SearchAction>();
        operators.addElement(new GirarDerecha());	
        operators.addElement(new GirarIzquierda());	
        operators.addElement(new Avanzar());	

        // Create the Problem which the agent will resolve
        Problem problem = new Problem(agGoal, agState, operators);
        this.setProblem(problem);
    }

    /**
     * This method is executed by the simulator to ask the agent for an action.
     */
    @Override
    public Action selectAction() {
    	
    	
    	if(solucion == null) {
    		solucion = buscarSolucion();
    	}
    	
    	Action a = null;
    	if(solucion != null) {
    		a = solucion.firstElement().getAction();
    		solucion.removeElementAt(0);
    	}
    	
    	System.out.println("Accion: " + a);
        System.out.println();
    	
        // Return the selected action
        return a;

    }
    
    private Vector<NTree> buscarSolucion() {
    	EstadoAgente ea = (EstadoAgente) this.getAgentState();
    	Entorno e = ea.getEntorno();
    	List<Par> objetivos = new ArrayList<Par>();
    	objetivos.addAll(e.getSalidas());
    	objetivos.addAll(e.getCerrojos());
    	List<Vector<NTree>> soluciones = new ArrayList<Vector<NTree>>();
        int tipoArbol = Search.EFAIA_TREE;
    	
        Problem problem = new Problem(
        		this.getProblem().getGoalState(),
        		this.getProblem().getAgentState(),
        		this.getProblem().getActions());
    	IStepCostFunction cost;
        IEstimatedCostFunction heuristic; 
        AStarSearch strategy;
        Search searchSolver;
        
        Strategy strategy2;

        //creamos un arbol para cada objetivo
        for (Par objetivo : objetivos) {
        	problem.setGoalState(new SalidaSinLlave());
        	problem.setAgentState(ea);
        	
        	String obj = "una salida";
        	
        	Vector<NTree> solucion = new Vector<NTree>();
        	
        	//si el objetivo es un cerrojo
        	if(e.getCerrojos().contains(objetivo)) {
        		
        		System.out.println("Buscando una llave: " + e.getLlaves().get(0));
        		
        		//primero buscamos una llave
            	problem.setGoalState(new LlaveEnMano());
            	
    	        // Create the search strategy
    	        cost = new CostFunction();
    	        heuristic = new Heuristic(e.getLlaves().get(0)); //por ahora solo busca una llave
    	        strategy = new AStarSearch(cost, heuristic);
    	        strategy2 = new UniformCostSearch(new CostFunction());
    	
    	        // Create a Search object with the strategy
    	        searchSolver = new Search(strategy2);
    	
    	        /* Generate an XML file with the search tree. It can also be generated
    	         * in other formats like PDF with PDF_TREE */
    	        searchSolver.setVisibleTree(tipoArbol);
    	
    	        // Set the Search searchSolver.
    	        this.setSolver(searchSolver);
    	
    	    	// Ask the solver for the best action
    	        try {
    	            Action a = searchSolver.solve(new Object[]{problem});
    	            if(a != null)
    	            	//guardamos el camino a la llave
    	            	solucion.addAll(searchSolver.getBestPath());
    	        } catch (Exception ex) {
    	            Logger.getLogger(Agente.class.getName()).log(Level.SEVERE, null, ex);
    	        }
    	        
    	        //luego buscamos el camino hasta el cerrojo
    	        problem.setGoalState(new SalidaConLlave());
    	        problem.setAgentState(solucion.lastElement().getAgentState());
            	
    	        System.out.println("Solucion: " + solucion);
    	        System.out.println();
    	        
    	        obj = "un cerrojo";
        	}
        	
        	System.out.println("Buscando " + obj + ": " + objetivo);
        	
	        // Create the search strategy
	        cost = new CostFunction();
	        heuristic = new Heuristic(objetivo); 
	        strategy = new AStarSearch(cost, heuristic); 
	        strategy2 = new UniformCostSearch(new CostFunction());
	
	        // Create a Search object with the strategy
	        searchSolver = new Search(strategy2);
	
	        /* Generate an XML file with the search tree. It can also be generated
	         * in other formats like PDF with PDF_TREE */
	        searchSolver.setVisibleTree(tipoArbol);
	
	        // Set the Search searchSolver.
	        this.setSolver(searchSolver);
	
	    	// Ask the solver for the best action
	        try {
	            Action a = searchSolver.solve(new Object[]{problem});
	            if(a != null)
	            	//guardamos o agregamos el camino a la salida o cerrojo
	            	solucion.addAll(searchSolver.getBestPath());
	        } catch (Exception ex) {
	            Logger.getLogger(Agente.class.getName()).log(Level.SEVERE, null, ex);
	        }
	        
	        soluciones.add(solucion);
	        
	        System.out.println("Solucion: " + solucion);
	        System.out.println();
        }
        
        //buscamos la solucion con el camino mas corto
        int caminoMin = -1;
        Vector<NTree> solucionMin = null;
        for(Vector<NTree> sol : soluciones) {
        	if(caminoMin == -1)
        		caminoMin = sol.size();
        	if(sol.size() <= caminoMin)
        		solucionMin = sol;
        }
        
        System.out.println("Solucion: " + solucionMin);
        System.out.println();
        
        return solucionMin;
    }

    /**
     * This method is executed by the simulator to give the agent a perception.
     * Then it updates its state.
     * @param p
     */
    @Override
    public void see(Perception p) {
        this.getAgentState().updateState(p);
    }
}

    